/*
 * $Id: $
 */
package org.rcgwt.client.ui.grid.renderer;

import java.util.Iterator;

import org.rcgwt.client.ui.grid.internal.model.IColumnModel;
import org.rcgwt.client.ui.grid.internal.model.IGridModel;
import org.rcgwt.client.ui.grid.internal.model.IGroupModel;
import org.rcgwt.client.ui.grid.internal.model.IHeaderModel;
import org.rcgwt.client.ui.grid.internal.model.IViewModel;
import org.rcgwt.client.ui.grid.internal.model.IViewModelElements;
import org.rcgwt.client.ui.grid.theme.CssStyle;
import org.rcgwt.client.ui.grid.theme.IGridTheme;

import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.Node;
import com.google.gwt.dom.client.NodeList;
import com.google.gwt.dom.client.Style;
import com.google.gwt.dom.client.TableColElement;
import com.google.gwt.dom.client.TableElement;
import com.google.gwt.dom.client.Style.Unit;

/**
 * 
 * @author Olivier Oeuillot (latest modification by $Author: $)
 * @version $Revision: $ $Date: $
 */
public class DefaultViewRenderer extends AbstractRenderer<IViewModel> implements
        IViewRenderer {

    @Override
    protected CssStyle getCssStyle(IViewRenderContext viewRenderContext,
            IViewModel viewModel) {

        IGridTheme theme = viewRenderContext.getGridRenderContext().getTheme();

        return theme.getViewCssStyle(viewModel);
    }

    protected CssStyle getTableCssStyle(IViewRenderContext viewRenderContext,
            IViewModel viewModel) {
        IGridTheme theme = viewRenderContext.getGridRenderContext().getTheme();

        return theme.getTableStyleName(viewModel);
    }

    @Override
    protected void updateStateMask(IViewRenderContext viewRenderContext,
            Element element, IViewModel viewModel) {
        super.updateStateMask(viewRenderContext, element, viewModel);

        CssStyle tableCssStyle = getTableCssStyle(viewRenderContext, viewModel);
        if (tableCssStyle != null) {
            Element tableElement = viewModel.getViewModelElements()
                    .getTableElement();
            if (tableElement != null) {
                tableElement.setClassName(tableCssStyle.getClassName());
            }
        }
    }

    public Element render(IViewRenderContext viewRenderContext, Element parent,
            IViewModel viewModel, int renderedIndex) {

        Document document = parent.getOwnerDocument();

        Element viewElement = impl.createFocusable();

        Element keysEventsElement = viewElement;
        Element scrollEventElement = null;

        IHeaderModel headerModels[] = viewModel.listHeaderModels();

        if (headerModels == null || headerModels.length < 1) {
            Element tableElement = renderTable(viewRenderContext, viewElement,
                    viewModel);

            viewElement.appendChild(tableElement);

            updateContent(viewRenderContext, viewElement, viewModel);

            updateStateMask(viewRenderContext, viewElement, viewModel);

        } else {
            IGridTheme theme = viewRenderContext.getGridRenderContext()
                    .getTheme();

            IHeaderModel topHeader = viewModel
                    .getHeaderModelByPosition(IHeaderModel.Position.TOP);
            if (topHeader != null) {
                IHeaderRenderer headerRenderer = theme
                        .getHeaderRenderer(topHeader);

                Element horizontalHeaderElement = headerRenderer.render(
                        viewRenderContext, viewElement, topHeader,
                        IViewModel.TOP_HEADER);

                viewElement.appendChild(horizontalHeaderElement);
            }

            Element tableParent = viewElement;

            int viewHints = viewModel.getRenderHints();

            if ((viewHints & (IViewModel.HORIZONTAL_SCROLLBAR | IViewModel.VERTICAL_SCROLLBAR)) > 0) {
                scrollEventElement = document.createDivElement();

                CssStyle scrollableTableContainerCssStyle = theme
                        .getScrollableTableContainerCssStyle();

                if (scrollableTableContainerCssStyle != null) {
                    scrollEventElement
                            .setClassName(scrollableTableContainerCssStyle
                                    .getClassName());
                }

                viewElement.appendChild(scrollEventElement);

                tableParent = scrollEventElement;

                viewModel.getViewModelElements().setScrollableTableContainer(
                        scrollEventElement);
            }

            Element tableElement = renderTable(viewRenderContext, tableParent,
                    viewModel);
            tableParent.appendChild(tableElement);

            IHeaderModel bottomHeader = viewModel
                    .getHeaderModelByPosition(IHeaderModel.Position.BOTTOM);
            if (bottomHeader != null) {
                IHeaderRenderer headerRenderer = theme
                        .getHeaderRenderer(bottomHeader);

                Element horizontalHeaderElement = headerRenderer.render(
                        viewRenderContext, viewElement, topHeader,
                        IViewModel.BOTTOM_HEADER);

                viewElement.appendChild(horizontalHeaderElement);
            }

            update(viewRenderContext, viewElement, viewModel, renderedIndex);
        }

        viewRenderContext.getGridRenderContext().getGridModel().getGridWidget()
                .getGridModelController().installElementEventsListener(
                        viewRenderContext, viewModel, keysEventsElement,
                        scrollEventElement);

        return viewElement;
    }

    @Override
    public void update(IViewRenderContext viewRenderContext,
            Element viewElement, IViewModel viewModel, int renderedIndex) {
        super.update(viewRenderContext, viewElement, viewModel, renderedIndex);

        viewModel.getViewModelElements().setViewElement(viewElement);
    }

    public void updateLayout(IViewRenderContext viewRenderContext,
            IViewModel viewModel, Element viewElement, int innerViewWidth,
            int innerViewHeight, int totalColumnWidth, int[] columnWidths) {

        IGridModel gridModel = viewModel.getGridModel();

        IViewModelElements viewModelElements = viewModel.getViewModelElements();

        IGridTheme gridTheme = viewRenderContext.getGridRenderContext()
                .getTheme();

        Element scrollableTableContainer = viewModelElements
                .getScrollableTableContainer();

        viewModelElements.getTableElement().getStyle().setPropertyPx(
                Styles.WIDTH, totalColumnWidth);

        IColumnModel columnModels[] = gridModel.listColumnModels();
        int columnModelIndex = 0;
        for (IColumnModel columnModel : columnModels) {

            Element col = viewModelElements.getColumnElement(columnModel);
            if (col == null) {
                columnModelIndex++;
                continue;
            }

            TableColElement.as(col).setWidth(
                    String.valueOf(columnWidths[columnModelIndex++]));
        }

        if (scrollableTableContainer != null) {
            int w = innerViewWidth;
            int h = innerViewHeight;

            IHeaderModel headerModels[] = viewModel.listHeaderModels();

            for (IHeaderModel headerModel : headerModels) {
                IHeaderRenderer headerRenderer;
                Element headerElement;

                headerRenderer = gridTheme.getHeaderRenderer(headerModel);
                assert headerRenderer != null : "Header renderer is null !";

                headerElement = viewModelElements.getHeaderElement(headerModel);
                if (headerElement == null) {
                    continue;
                }

                int headerSize = headerRenderer.getSize(viewRenderContext,
                        headerModel, headerElement);
                int headerSeparatorSize = gridTheme
                        .getHeaderSeparatorSize(headerModel);

                if (headerModel.getOrientation() == IHeaderModel.Orientation.VERTICAL) {
                    w -= headerSize;
                    w -= headerSeparatorSize;

                    headerRenderer.updateLayout(viewRenderContext, headerModel,
                            headerElement, headerSize, innerViewHeight,
                            totalColumnWidth, columnWidths);

                } else {
                    h -= headerSize;
                    h -= headerSeparatorSize;

                    headerRenderer.updateLayout(viewRenderContext, headerModel,
                            headerElement, innerViewWidth, headerSize,
                            totalColumnWidth, columnWidths);
                }

            }

            scrollableTableContainer.getStyle().setPropertyPx(Styles.WIDTH, w);

            scrollableTableContainer.getStyle().setPropertyPx(Styles.HEIGHT, h);
        }
    }

    protected Element renderTable(IViewRenderContext viewRenderContext,
            Element parent, IViewModel viewModel) {

        IGridTheme gridTheme = viewRenderContext.getGridRenderContext()
                .getTheme();

        Document document = parent.getOwnerDocument();

        TableElement tableElement = document.createTableElement();
        tableElement.setCellPadding(gridTheme.getDataTableCellPadding());
        tableElement.setCellSpacing(gridTheme.getDataTableCellSpacing());

        int margins[] = gridTheme.getTableMargins();
        Style tableStyle = tableElement.getStyle();
        tableStyle.setMarginTop(margins[0], Unit.PX);
        tableStyle.setMarginRight(margins[1], Unit.PX);
        tableStyle.setMarginBottom(margins[2], Unit.PX);
        tableStyle.setMarginLeft(margins[3], Unit.PX);

        viewModel.getViewModelElements().setTableElement(tableElement);

        return tableElement;
    }

    @SuppressWarnings("boxing")
    @Override
    protected void updateContent(IViewRenderContext viewRenderContext,
            Element viewElement, IViewModel viewModel) {

        IViewModelElements viewModelElements = viewModel.getViewModelElements();

        IGridRenderContext gridRenderContext = viewRenderContext
                .getGridRenderContext();

        IGridModel gridModel = viewModel.getGridModel();

        IGridTheme theme = gridRenderContext.getTheme();

        viewModelElements.clearGroupAndColumnsElements(viewModel, viewElement);

        TableElement table = TableElement.as(viewModelElements
                .getTableElement());

        NodeList<Node> nodes = null;
        int childrenCount = 0;

        if (table.hasChildNodes()) {
            nodes = table.getChildNodes();
            childrenCount = nodes.getLength();
        }

        int childrenIndex = 0;

        int renderedIndex = 0;
        Element columnInsertElement = null;

        int columnModelIndexes[] = gridRenderContext.getColumnModelIndexes();
        IColumnModel columnModels[] = gridModel.listColumnModels();

        Document document = viewElement.getOwnerDocument();

        for (int i = 0; i < columnModelIndexes.length; i++) {
            IColumnModel columnModel = columnModels[columnModelIndexes[i]];

            TableColElement colElement = null;

            for (; columnInsertElement == null && childrenIndex < childrenCount;) {
                Node child = nodes.getItem(childrenIndex);

                if (child.getNodeType() != Node.ELEMENT_NODE) {
                    table.removeChild(child);
                    continue;
                }

                childrenIndex++;

                Element element = Element.as(child);

                if ("col".equalsIgnoreCase(element.getTagName())) {
                    colElement = TableColElement.as(element);
                    break;
                }

                columnInsertElement = element;
            }

            if (colElement == null) {
                colElement = document.createColElement();

                table.insertBefore(colElement, columnInsertElement);
            }

            viewModelElements.setColumnElement(columnModel, colElement,
                    renderedIndex++);
        }

        Iterator<Integer> groupModelIndexIterator = gridModel.getGridWidget()
                .getGroupDataModel().getGroupModelIndexes(0, -1);

        IGroupModel groupModels[] = gridModel.listGroupModels();

        renderedIndex = 0;
        for (; groupModelIndexIterator.hasNext();) {
            int groupModelIndex = groupModelIndexIterator.next();

            IGroupModel groupModel = groupModels[groupModelIndex];

            IGroupRenderer groupRenderer = theme.getGroupRenderer(groupModel);

            Element groupElement = null;

            for (; childrenIndex < childrenCount;) {
                Node child = nodes.getItem(childrenIndex);

                if (child.getNodeType() != Node.ELEMENT_NODE) {
                    table.removeChild(child);
                    continue;
                }

                childrenIndex++;

                groupElement = Element.as(child);
            }

            if (groupElement != null) {
                groupRenderer.update(viewRenderContext, groupElement,
                        groupModel, renderedIndex);

                continue;
            }

            groupElement = groupRenderer.render(viewRenderContext, table,
                    groupModel, renderedIndex++);

            table.appendChild(groupElement);
        }

        // Delete unused groupElements
        while (childrenIndex < childrenCount) {
            table.removeChild(nodes.getItem(childrenIndex));
            childrenCount--;
        }
    }

    @Override
    public int[] getSizes(IViewRenderContext viewRenderContext,
            Element element, IViewModel modelElement, int[] sizes) {

        Element scrollableContainer = modelElement.getViewModelElements()
                .getScrollableTableContainer();
        if (scrollableContainer != null) {

            if (sizes == null || sizes.length < 2) {
                sizes = new int[2];
            }

            sizes[0] = scrollableContainer.getClientWidth();
            sizes[1] = scrollableContainer.getClientHeight();

            return sizes;
        }

        return super.getSizes(viewRenderContext, element, modelElement, sizes);
    }

    @Override
    public void setScrollPositions(IViewRenderContext viewRenderContext,
            Element element, IViewModel modelElement, int scrollLeft,
            int scrollTop) {
        Element scrollableContainer = modelElement.getViewModelElements()
                .getScrollableTableContainer();
        if (scrollableContainer != null) {
            scrollableContainer.setScrollLeft(scrollLeft);
            scrollableContainer.setScrollTop(scrollTop);
        }
    }

    @Override
    public void setFocus(IViewRenderContext viewRenderContext,
            IViewModel viewModel, Element viewElement) {
        impl.focus((com.google.gwt.user.client.Element) viewElement);
    }

}
